package com.block.datapush.service.apipush.utils;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
 * 期待 RAS加密
 */

public class QiDaiRsaUtil {
    private static final String KEY_ALGORITHM = "RSA";
    public static final String DEFAULT_ALGORITHM = "MD5withRSA";
    private static final String DEFAULT_ENCODING = "UTF-8";

    public static String sign(String privateKey, String dataSrc) {
        return sign(privateKey, dataSrc, DEFAULT_ALGORITHM);
    }

    /**
     * 签名
     *
     * @param privateKey：私钥
     * @param dataSrc：源
     * @param algorithm：签名算法
     * @return 签名
     */
    public static String sign(String privateKey, String dataSrc, String algorithm) {
        try {
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            PrivateKey myPrivateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            Signature signature = Signature.getInstance(algorithm);
            signature.initSign(myPrivateKey);
            signature.update(dataSrc.getBytes(DEFAULT_ENCODING));
            byte[] signed = signature.sign();
            return Base64.encodeBase64String(signed);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 签名验证
     *
     * @param publicKey：公钥
     * @param dataSrc：源
     * @param sign：签名结果串
     * @return 验签结果
     */
    public static boolean checkSign(String publicKey, String dataSrc, String sign) throws Exception {
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey myPublicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        byte[] signed = Base64.decodeBase64(sign);
        Signature signature = Signature.getInstance(DEFAULT_ALGORITHM);
        signature.initVerify(myPublicKey);
        signature.update(dataSrc.getBytes(DEFAULT_ENCODING));
        return signature.verify(signed);
    }

    private static PublicKey getPublicKey(String idRsaPub) throws NoSuchAlgorithmException, InvalidKeySpecException {
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(idRsaPub));
        KeyFactory keyFactory;
        keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return keyFactory.generatePublic(x509EncodedKeySpec);
    }

    private static PrivateKey getPrivateKey(String idRsa) throws NoSuchAlgorithmException, InvalidKeySpecException {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(idRsa));
        KeyFactory keyFactory1;
        keyFactory1 = KeyFactory.getInstance(KEY_ALGORITHM);
        return keyFactory1.generatePrivate(pkcs8EncodedKeySpec);
    }

    private static byte[] decryptByPrivateKey(byte[] encryptedData, String idRsa) throws Exception {
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(2, getPrivateKey(idRsa));
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;

        for (int i = 0; inputLen - offSet > 0; offSet = i * 256) {
            byte[] cache;
            if (inputLen - offSet > 256) {
                cache = cipher.doFinal(encryptedData, offSet, 256);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }

            out.write(cache, 0, cache.length);
            ++i;
        }

        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }

    public static String decryptByPrivateKey(String data, String idRsa) throws Exception {
        byte[] encryptedData = Base64.decodeBase64(data);
        byte[] result = decryptByPrivateKey(encryptedData, idRsa);
        if (result == null) {
            return null;
        }
        return new String(result, StandardCharsets.UTF_8);
    }

    public static String encryptByPublicKey(String data, String idRsaPub) throws Exception {
        byte[] encryptedData = data.getBytes(StandardCharsets.UTF_8);
        byte[] result = encryptByPublicKey(encryptedData, idRsaPub);
        if (result == null) {
            return null;
        }
        return Base64.encodeBase64String(result);
    }

    private static byte[] encryptByPublicKey(byte[] data, String idRsaPub) throws Exception {
        PublicKey publicKey = getPublicKey(idRsaPub);
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
        cipher.init(1, publicKey);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;

        for (int i = 0; inputLen - offSet > 0; offSet = i * 244) {
            byte[] cache;
            if (inputLen - offSet > 244) {
                cache = cipher.doFinal(data, offSet, 244);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }

            out.write(cache, 0, cache.length);
            ++i;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }
}
